/*
 * Copyright 2010 (c) Dou Yongwang (douyongwang@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */


#include <e32cons.h>
#include <f32file.h>

#include <sjson.h>

#include "FacebookObject.h"

_LIT(kJSONTest, "JSON-Test");
_LIT(KTxtTestSJON, "TestJSON");
_LIT(KFormatFailed,"failed: leave code=%d");
_LIT(KTxtOK,"ok");
_LIT(KTxtPressAnyKey," [press any key]");

// public
LOCAL_D CConsoleBase* console; // write all your messages to this
using namespace sjson;

LOCAL_C void show(const TDesC8& aDesC)
	{
	HBufC* json2 = HBufC::NewLC(aDesC.Length());
	json2->Des().Copy(aDesC);
	console->Printf(*json2);
	CleanupStack::PopAndDestroy(json2); //
	}

LOCAL_C void testParserL()
	{
	CJSONValue* v0 = CJSONValue::NewL();
#if 1
		// Test
		{
		_LIT8(kTestStr, "\"field1\"");
		CJSONValue& v=*v0;
		ASSERT(v.Parse(kTestStr()) > 0);
		ASSERT(v.Is<TDesC8>());
		const TDesC8& val = v.Get<TDesC8>();
		ASSERT(val.Compare(_L8("field1")) == 0);
		}
#endif
#if 1
		{
		_LIT8(kTestStr,"true");
		CJSONValue& v=*v0;
		ASSERT(v.Parse(kTestStr()) > 0);
		ASSERT(v.Is<TBool>());
		ASSERT(v.Get<TBool>());
		}
#endif
#if 1
		{
		_LIT8(kTestStr,"false");
		CJSONValue& v=*v0;
		ASSERT(v.Parse(kTestStr()) > 0);
		ASSERT(v.Is<TBool>());
		ASSERT(!v.Get<TBool>());
		}
#endif
#if 1
		{
		_LIT8(kTestStr,"null");
		CJSONValue& v=*v0;
		ASSERT(v.Parse(kTestStr()) > 0);
		ASSERT(v.Is<CJSONValue::Null>());
		}
#endif
		delete v0;
#if 1
		{
		_LIT8(kTestStr,"[\"field1\", 6]");
		CJSONArray* a0 = CJSONArray::NewL();
		CJSONArray& v=*a0;
		TInt ret = v.Parse(kTestStr());
		ASSERT(ret > 0);
		ASSERT(v.Get<TDesC8>(0).Compare(_L8("field1")) == 0);
        ASSERT(v.Has<TInt64>(1));
        ASSERT(6 == v.Get<TInt64>(1));
        ASSERT(!v.Has<TBool>(2));
        delete a0;
		}
#endif
#if 1
		{
		_LIT8(kTestStr,"{"
                "  \"foo\" : 1,"
                "  \"bar\" : false,"
                "  \"person\" : {\"name\" : \"GWB\", \"age\" : 60},"
                "  \"data\": [\"abcd\", 42]"
                "}");
		CJSONObject* o0 = CJSONObject::NewL();
		CJSONObject& o=*o0;
		TInt ret = o.Parse(kTestStr());
		ASSERT(ret == kTestStr().Length());
        ASSERT(1 == o.Get<TInt64>(_L8("foo")));
        ASSERT(o.Has<TBool>(_L8("bar")));
        ASSERT(o.Has<CJSONObject>(_L8("person")));
        ASSERT(o.Get<CJSONObject>(_L8("person")).Has<TInt64>(_L8("age")));
        ASSERT(o.Has<CJSONArray>(_L8("data")));
        ASSERT(o.Get<CJSONArray>(_L8("data")).Get<TInt64>(1) == 42);
        ASSERT(o.Get<CJSONArray>(_L8("data")).Get<TDesC8>(0) == _L8("abcd"));
        ASSERT(!o.Has<TInt64>(_L8("data")));
        delete o0;
		}
#endif
	}

LOCAL_C void testParseComplexL()
	{
	_LIT8(kTestStr,"["
            "{"
            "  \"foo\" : 1,"
            "  \"bar\" : false,"
            "  \"person\" : {\"name\" : \"GWB\", \"age\" : 60},"
            "  \"data\": [\"abcd\", 42]"
            "},"
            "{"
            "  \"foo\" : 2,"
            "  \"bar\" : true,"
            "  \"person\" : {\"name\" : \"Amy\", \"age\" : 20},"
            "  \"data\": [\"abcd\", 54]"
            "}"
            "]");
	CJSONValue* v0 = CJSONValue::NewL();
    CJSONValue& vv = *v0;
    TInt ret = vv.Parse(kTestStr);
    ASSERT(ret == kTestStr().Length());
    CJSONArray& v = vv.Get<CJSONArray>();

    ASSERT(v.Has<CJSONObject>(0));
    ASSERT(v.Has<CJSONObject>(1));
		{
        CJSONObject& o = v.Get<CJSONObject>(0);
        ASSERT(1 == o.Get<TInt64>(_L8("foo")));
        ASSERT(o.Has<TBool>(_L8("bar")));
        ASSERT(o.Has<CJSONObject>(_L8("person")));
//            ASSERT(o.Get<CJSONObject>(_L8("person")).Has<TInt64>(_L8("age"));
        ASSERT(o.Get<CJSONObject>(_L8("person")).Has<TInt64>(_L8("age")));
        ASSERT(o.Has<CJSONArray>(_L8("data")));
        ASSERT(o.Get<CJSONArray>(_L8("data")).Get<TInt64>(1) == 42);
        ASSERT(o.Get<CJSONArray>(_L8("data")).Get<TDesC8>(0) == _L8("abcd"));
        ASSERT(!o.Has<TInt64>(_L8("data")));
		}
		{
        CJSONObject& o = v.Get<CJSONObject>(1);
        ASSERT(2 == o.Get<TInt64>(_L8("foo")));
        ASSERT(o.Has<TBool>(_L8("bar")));
        ASSERT(o.Has<CJSONObject>(_L8("person")));
        ASSERT(o.Get<CJSONObject>(_L8("person")).Has<TInt64>(_L8("age")));
        ASSERT(o.Has<CJSONArray>(_L8("data")));
        ASSERT(o.Get<CJSONArray>(_L8("data")).Get<TInt64>(1) == 54);
        ASSERT(o.Get<CJSONArray>(_L8("data")).Get<TDesC8>(0) == _L8("abcd"));
        ASSERT(!o.Has<TInt64>(_L8("data")));
		}
	delete v0;
	}

LOCAL_C void testConvertToJSONStringL()
	{
	using namespace sjson;
	CJSONObject* o0 = CJSONObject::NewL();
	CJSONObject& o=*o0;
	TInt64 val = 100;
	o.Add(_L8("callid"), val);
	o.Add(_L8("session"), _L8("12345678"));
	HBufC8* json = o.ToJSONStringLC();
	show(*json);
	CleanupStack::PopAndDestroy(json);
	delete o0;
	}

LOCAL_C void testFacebookFriendsListsL()
	{
	_LIT8(kTestStr,"[{\"flid\":92083696741,\"name\":\"Google\"}]");
	CJSONValue* v0 = CJSONValue::NewL();
	CJSONValue& vv = *v0;
	TInt ret = vv.Parse(kTestStr);
	ASSERT(ret == kTestStr().Length());
	CJSONArray& v = vv.Get<CJSONArray>();
    ASSERT(v.Has<CJSONObject>(0));
		{
        CJSONObject& o = v.Get<CJSONObject>(0);
        ASSERT(92083696741 == o.Get<TInt64>(_L8("flid")));
        ASSERT(o.Has<TDesC8>(_L8("name")));
		}
	delete v0;
	}

LOCAL_C void testEmptyObjectL()
	{
	_LIT8(kUnread, "unread");
	_LIT8(kFriendsRequest, "friend_requests");
	_LIT8(kMessages, "messages");
	_LIT8(kPokes, "pokes");

	_LIT8(kTestStr,"{\"messages\":{\"unread\":0,\"most_recent\":1240754204},\"pokes\":{\"unread\":0,\"most_recent\":0},\"shares\":{\"unread\":0,\"most_recent\":0},\"friend_requests\":{},\"group_invites\":{},\"event_invites\":{}}");
//	_LIT8(kTestStr,"{\"friend_requests\":{},\"group_invites\":{},\"event_invites\":{}}");
//	_LIT8(kTestStr,"{}");
	CJSONValue* v0 = CJSONValue::NewL();
	CJSONValue& vv=*v0;
	TInt ret = vv.Parse(kTestStr);
	ASSERT(ret == kTestStr().Length());
	ASSERT(vv.Is<CJSONObject>());
	CJSONObject& o = vv.Get<CJSONObject>();
	{
	// friend_requests
	ASSERT(o.Has<CJSONObject>(_L8("friend_requests")));
	CJSONObject& friend_requests = o.Get<CJSONObject>(kFriendsRequest());
	ASSERT(!friend_requests.Has<TDesC8>(kUnread()));
	}
	{
	// kMessages
	ASSERT(o.Has<CJSONObject>(kMessages()));
	CJSONObject& messages = o.Get<CJSONObject>(kMessages());
	ASSERT(messages.Has<TInt64>(kUnread()));
	ASSERT(messages.Get<TInt64>(kUnread()) == 0);
	}
	{
	// kPokes
	ASSERT(o.Has<CJSONObject>(kPokes()));
	CJSONObject& pokes = o.Get<CJSONObject>(kPokes());
	ASSERT(pokes.Has<TInt64>(kUnread()));
	ASSERT(pokes.Get<TInt64>(kUnread()) == 0);
	}
//	ASSERT(o.Has<CJSONObject>(_L8("group_invites")));
	{
	CFacebookObject* fbObject = CFacebookObject::NewLC(&o);
	CFacebookObject* messages = CFacebookObject::NewLC(NULL);
	TInt ret = fbObject->getObject(kMessages(), *messages);
	if (ret == KErrNone)
		{
		TInt64 unread = -1;
		ret = messages->getInt64(kUnread(), unread);
		ASSERT(ret == KErrNone);
		}
	CleanupStack::PopAndDestroy(messages);
	CleanupStack::PopAndDestroy(fbObject);
	}
	delete v0;
	}

LOCAL_C void testEmptyArrayL()
	{
	CJSONValue* v0 = CJSONValue::NewL();
	{
	_LIT8(kTestStr,"{\"wasAvailableIDs\":[]}");
	CJSONValue& vv=*v0;
	TInt ret = vv.Parse(kTestStr);
	ASSERT(ret >= 0);
	ASSERT(vv.Is<CJSONObject>());
	CJSONObject& o = vv.Get<CJSONObject>();
	ASSERT(o.Has<CJSONArray>(_L8("wasAvailableIDs")));
	CJSONArray& arr = o.Get<CJSONArray>(_L8("wasAvailableIDs"));
	ASSERT(arr.Size() == 0);
	}
	{
	_LIT8(kTestStr,"{\"onload\":[\"if (window.Env) { Env[\"nctrlid\"]=\"9f5448099134129df58fe65821a49f48\"; }\",\"if (window.Env) { Env[\"nctrlnid\"]=\"\"; }\"],\"invalidate_cache\":[0]}");

	CJSONValue& vv=*v0;
	TInt ret = vv.Parse(kTestStr);
	ASSERT(ret >= 0);

	}
	delete v0;
	}

LOCAL_C void testFbLinkReturnsL()
	{
	CJSONValue* v0 = CJSONValue::NewL();
	{
	_LIT8(kTitleStr,"\"Thailand tourist's lung tissue 'gone' after mysterious death - CNN.com\"");
	CJSONValue& vv=*v0;
	TInt ret = vv.Parse(kTitleStr);
	ASSERT(ret == kTitleStr().Length());
	}
	{
	_LIT8(kSummaryStr,"\"A  \\\"lungs were 100 percent congested,\\\" Jill .\"");
	CJSONValue& vv=*v0;
	TInt ret = vv.Parse(kSummaryStr);
	ASSERT(ret == kSummaryStr().Length());
	}
	{
	_LIT8(kUrlStr,"\"http:\\/\\/edition.cnn.com\\/2009\\/WORLD\\/asiapcf\\/05\\/29\\/thailand.mysterious.deaths\\/index.html\"");
	CJSONValue& vv=*v0;
	TInt ret = vv.Parse(kUrlStr);
	ASSERT(ret == kUrlStr().Length());
	}
	{
	_LIT8(kImageUrls, "[\"http:\\/\\/i.cdn.turner.com\\/cnn\\/2009\\/WORLD\\/meast\\/05\\/30\\/iraq.arrest\\/tzmos.sudani.afp.gi.jpg\"]");
	CJSONValue& vv=*v0;
	TInt ret = vv.Parse(kImageUrls);
	ASSERT(ret == kImageUrls().Length());
	}
	delete v0;
	}

LOCAL_C void testDecodeEncodeing()
	{
#if 1
	{
	// test 1: western string
	_LIT8(kTestValue, "Tati ovog djeteta \\u0107e se ostvariti san skoro "
			"svakog tate....sin \\u0107e ti postiti nogometa\\u0161");
	HBufC8* val = DecodeJSONString(kTestValue);
	HBufC8* val2 = EncodeJSONString(*val, EFalse);
	ASSERT(val2->Des().Compare(kTestValue) == 0);
	delete val2;
	delete val;
	}
#endif
	{
	// test 2: test Chinese string
	_LIT8(kTestValue, "test in Chinese \\u4e2d\\u6587\\u5185\\u5bb9\\u6d4b\\u8bd5");
	HBufC8* val = DecodeJSONString(kTestValue);
	HBufC8* val2 = EncodeJSONString(*val, EFalse);
	ASSERT(val2->Des().Compare(kTestValue) == 0);
	delete val2;
	delete val;
	}
	}
HBufC8* LoadFileL(const TDesC& aFileName)
	{
	RFs fs;
	User::LeaveIfError(fs.Connect());
	CleanupClosePushL(fs);
	RFile file;
	User::LeaveIfError(file.Open(fs, aFileName, EFileShareAny|EFileRead));
	CleanupClosePushL(file);
	TInt size;
	file.Size(size);
	HBufC8* buf = HBufC8::NewLC(size);
	TPtr8 ptr =buf->Des();
	file.Read(ptr, size);
	CleanupStack::Pop(buf);
	CleanupStack::PopAndDestroy(2, &fs);
	return buf;
	}

LOCAL_C void testParseFileL()
	{
	// WARNING: to run this test, put a file named "sample.json" under c: in your disk 
	HBufC8* jsonData = LoadFileL(_L("c:\\sample.json"));
	CleanupStack::PushL(jsonData);
	CJSONValue* value = CJSONValue::NewL();
	CleanupStack::PushL(value);
	TInt ret = value->Parse(*jsonData);
	User::LeaveIfError(ret);
	CleanupStack::PopAndDestroy(value);
	CleanupStack::PopAndDestroy(jsonData);
	}

LOCAL_C void doTestsL()
	{
//	testFbLinkReturnsL();
//	testDecodeEncodeing();
//	testEmptyArrayL();
	testEmptyObjectL();
//	testFacebookFriendsListsL();
//	testParseComplexL();
//	testParseFileL();
//	testParserL();
//	testConvertToJSONStringL();
	}

LOCAL_C void callExampleL() // initialize and call example code under cleanup stack
    {
	console=Console::NewL(KTxtTestSJON,TSize(KConsFullScreen,KConsFullScreen));
	CleanupStack::PushL(console);
	TRAPD(error,doTestsL()); // perform example function
	if (error)
		console->Printf(KFormatFailed, error);
	else
		console->Printf(KTxtOK);
	console->Printf(KTxtPressAnyKey);
	console->Getch(); // get and ignore character
	CleanupStack::PopAndDestroy(); // close console
    }

GLDEF_C TInt E32Main() // main function called by E32
    {
	__UHEAP_MARK;
	CTrapCleanup* cleanup=CTrapCleanup::New(); // get clean-up stack
	TRAPD(error,callExampleL()); // more initialization, then do example
	__ASSERT_ALWAYS(!error,User::Panic(kJSONTest, error));
	delete cleanup; // destroy clean-up stack
	__UHEAP_MARKEND;
	return 0; // and return
    }
